package com.gitee.wsl.result

import com.gitee.wsl.ext.base.ifValue
import com.gitee.wsl.result.sample.ValueResult.Companion.Ok
import com.gitee.wsl.result.sample.ValueResult
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract


interface EntryResult<out V, out E> {
    @Suppress("UNCHECKED_CAST")
    val value: V

    @Suppress("UNCHECKED_CAST")
    val error: E

    val isOk: Boolean

    val isErr: Boolean get() = !isOk

    operator fun component1(): V? {
        return isOk.ifValue(value,null)
    }

    operator fun component2(): E?{
        return isErr.ifValue(error,null)
    }

    fun getErrorOrNull():E? = isErr.ifValue(error,null)
}

internal class Failure<out E>(
    val error: E,
) {
    override fun equals(other: Any?): Boolean {
        return other is Failure<*> && error == other.error
    }

    override fun hashCode(): Int {
        return error.hashCode()
    }

    override fun toString(): String {
        return "Failure($error)"
    }
}

internal class StateEntry<out E>(
    val stateValue: E,
) {
    override fun equals(other: Any?): Boolean {
        return other is StateEntry<*> && stateValue == other.stateValue
    }

    override fun hashCode(): Int {
        return stateValue.hashCode()
    }

    override fun toString(): String {
        return "State($stateValue)"
    }
}

@OptIn(ExperimentalContracts::class)
inline fun < V,  E, T:EntryResult< V, E>> T.onSuccess(action: (value: V) -> Unit): T {
    contract {
        callsInPlace(action, InvocationKind.AT_MOST_ONCE)
    }
    if (this.isOk) action(value)
    return this
}

@OptIn(ExperimentalContracts::class)
inline fun < V,  E, T:EntryResult< V, E>> T.onFailure(action: (exception: E) -> Unit): T {
    contract {
        callsInPlace(action, InvocationKind.AT_MOST_ONCE)
    }
    if (this.isErr) action(this.error)
    return this
}

@OptIn(ExperimentalContracts::class)
inline fun <R, T : R, E> EntryResult<T,E>.recover(transform: (exception: E) -> R):  EntryResult< R, E>  {
    contract {
        callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
    }
    return if (this.isErr) Ok(transform(error)) else this
}



/**
 * Unsafely casts this [Result<V, E>][ValueResult] to [Result<U, Nothing>][ValueResult], to be used inside
 * an explicit [isOk][ValueResult.isOk] or [isErr][ValueResult.isErr] guard.
 */
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
inline fun <V, E, U> EntryResult<V, E>.asOk(): EntryResult<U, Nothing> {
    return this as EntryResult<U, Nothing>
}

/**
 * Unsafely casts this [Result<V, E>][ValueResult] to [Result<Nothing, F>][ValueResult], to be used inside
 * an explicit [isOk][ValueResult.isOk] or [isErr][ValueResult.isErr] guard.
 */
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
inline fun <V, E, F> EntryResult<V, E>.asErr(): EntryResult<Nothing, F> {
    return this as EntryResult<Nothing, F>
}

